////////////////////////////////////////////////////////////////////////////////////////////
//GraphManager.cpp
//
//Copyright 2010 Eric Callahan. All rights reserved.
//
//Redistribution and use in source and binary forms, with or without modification, are
//permitted provided that the following conditions are met:
//
//   1. Redistributions of source code must retain the above copyright notice, this list of
//      conditions and the following disclaimer.
//
//   2. Redistributions in binary form must reproduce the above copyright notice, this list
//      of conditions and the following disclaimer in the documentation and/or other materials
//      provided with the distribution.
//
//THIS SOFTWARE IS PROVIDED BY ERIC CALLAHAN ``AS IS'' AND ANY EXPRESS OR IMPLIED
//WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
//FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ERIC CALLAHAN OR
//CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
//CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
//SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
//ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
//ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//The views and conclusions contained in the software and documentation are those of the
//authors and should not be interpreted as representing official policies, either expressed
//or implied, of Eric Callahan.
////////////////////////////////////////////////////////////////////////////////////////////

#include "GraphManager.h"

// Stupid MS forgot to define this external in strmiids.lib (for the Windows 7.0+ SDK)
EXTERN_C const IID IID_IMPEG2PIDMap = __uuidof(IMPEG2PIDMap); 


GraphManager::GraphManager(HWND hwndVideo, CodecManager* codecMan) :
	m_playState(STATE_CLOSED),
	m_hwndVideo(hwndVideo),
	m_hwndEvent(NULL),
	m_eventMsg(0),
	m_pGraph(NULL),
	m_pControl(NULL),
	m_pEvent(NULL),
	m_pAudio(NULL),
    m_pVideo(NULL),
	m_bMute(FALSE),
	m_bAudioStream(FALSE),
	m_lVolume(MAX_VOLUME),
	m_TokenROT(0),
	m_codecMan(codecMan),
	m_curAudDec(NULL),
	m_curVidDec(NULL),
	m_curXBar(NULL),
	m_curCapDevice(NULL),
	m_curEncoder(NULL)
{
}

GraphManager::~GraphManager()
{
	TearDownGraph();
}

//-----------------------------------------------------------------------------
// GraphManager::SetEventWindow
// Description: Set the window to receive graph events.
//
// hwnd: Window to receive the events.
// msg: Private window message that window will receive whenever a 
//      graph event occurs. (Must be in the range WM_APP through 0xBFFF.)
//-----------------------------------------------------------------------------
HRESULT GraphManager::SetEventWindow(HWND hwnd, UINT msg)
{
	m_hwndEvent = hwnd;
	m_eventMsg = msg;
	return S_OK;
}

//-----------------------------------------------------------------------------
// GraphManager::OpenGraph
// Description: Open a new file for playback.
//-----------------------------------------------------------------------------
HRESULT GraphManager::OpenGraph()
{
	HRESULT hr = S_OK;

	// Create a new filter graph. (This also closes the old one, if any.)
	hr = InitializeGraph();
	
	// Build the capture device graph
	if (SUCCEEDED(hr))
	{
		hr = this->SetupCaptureGraph();
	}

    // Set the volume.
	if (SUCCEEDED(hr))
	{
		hr = UpdateVolume();
	}

	// Update our state.
	if (SUCCEEDED(hr))
	{
		m_playState = STATE_STOPPED;
	}

	// Add Graph to Running objects table for debugging
	AddGraphToRot((IUnknown*)m_pGraph, &m_TokenROT);

	return hr;
}

void GraphManager::CloseGraph()
{
	TearDownGraph();
}


//-----------------------------------------------------------------------------
// GraphManger::HandleGraphEvent
// Description: Respond to a graph event.
//
// The owning window should call this method when it receives the window
// message that the application specified when it called SetEventWindow.
//
// pCB: Pointer to the GraphEventCallback callback, implemented by 
//      the application. This callback is invoked once for each event
//      in the queue. 
//
// Caution: Do not tear down the graph from inside the callback.
//-----------------------------------------------------------------------------

HRESULT GraphManager::HandleGraphEvent(GraphEventCallback *pCB)
{
	if (pCB == NULL)
	{
		return E_POINTER;
	}

	if (!m_pEvent)
	{
		return E_UNEXPECTED;
	}

	long evCode = 0;
	LONG_PTR param1 = 0, param2 = 0;

	HRESULT hr = S_OK;

    // Get the events from the queue.
	while (SUCCEEDED(m_pEvent->GetEvent(&evCode, &param1, &param2, 0)))
	{
        // Invoke the callback.
		pCB->OnGraphEvent(evCode, param1, param2);

        // Free the event data.
		hr = m_pEvent->FreeEventParams(evCode, param1, param2);
		if (FAILED(hr))
		{
			break;
		}
	}

	return hr;
}

// state changes
HRESULT GraphManager::Play()
{
	if (m_playState != STATE_PAUSED && m_playState != STATE_STOPPED)
	{
		return VFW_E_WRONG_STATE;
	}

	assert(m_pGraph); // If state is correct, the graph should exist.

	HRESULT hr = m_pControl->Run();

	if (SUCCEEDED(hr))
	{
		m_playState = STATE_RUNNING;
	}

	return hr;
}

HRESULT GraphManager::Stop()
{
	if (m_playState != STATE_RUNNING && m_playState != STATE_PAUSED)
	{
		return VFW_E_WRONG_STATE;
	}

	assert(m_pGraph); // If state is correct, the graph should exist.

	HRESULT hr = m_pControl->Stop();

	if (SUCCEEDED(hr))
	{
		m_playState = STATE_STOPPED;
	}
	
	return hr;
}

// EVR/VMR functionality
BOOL GraphManager::HasVideo() const
{
    return (m_pVideo && m_pVideo->HasVideo());
}

//-----------------------------------------------------------------------------
// GraphManager::UpdateVideoWindow
// Description: Sets the destination rectangle for the video.
//-----------------------------------------------------------------------------
HRESULT GraphManager::UpdateVideoWindow(const LPRECT prc)
{
    HRESULT hr = S_OK;

    if (m_pVideo)
    {
        hr = m_pVideo->UpdateVideoWindow(m_hwndVideo, prc);
    }

    return hr;
}

//-----------------------------------------------------------------------------
// GraphManager::Repaint
// Description: Repaints the video.
//
// Call this method when the application receives WM_PAINT.
//-----------------------------------------------------------------------------
HRESULT GraphManager::Repaint(HDC hdc)
{
    HRESULT hr = S_OK;

    if (m_pVideo)
    {
        hr = m_pVideo->Repaint(m_hwndVideo, hdc);
    }

    return hr;
}

//-----------------------------------------------------------------------------
// GraphManager::DisplayModeChanged
// Description: Notifies the VMR that the display mode changed.
//
// Call this method when the application receives WM_DISPLAYCHANGE.
//-----------------------------------------------------------------------------
HRESULT GraphManager::DisplayModeChanged()
{
    HRESULT hr = S_OK;

    if (m_pVideo)
    {
        hr = m_pVideo->DisplayModeChanged();
    }

    return hr;
}

//-----------------------------------------------------------------------------
// GraphManager::Mute
// Description: Mutes or unmutes the audio.
//-----------------------------------------------------------------------------
HRESULT	GraphManager::Mute(const BOOL& bMute)
{
	m_bMute = bMute;
	return UpdateVolume();
}

//-----------------------------------------------------------------------------
// GraphManager::SetVolume
// Description: Sets the volume. 
//-----------------------------------------------------------------------------
HRESULT	GraphManager::SetVolume(const long& lVol)
{
	m_lVolume = lVol;
	return UpdateVolume();
}


//-----------------------------------------------------------------------------
// GraphManager::UpdateVolume
// Description: Update the volume after a call to Mute() or SetVolume().
//-----------------------------------------------------------------------------
HRESULT GraphManager::UpdateVolume()
{
	HRESULT hr = S_OK;

	if (m_bAudioStream && m_pAudio)
	{
        // If the audio is muted, set the minimum volume. 
		if (m_bMute)
		{
			hr = m_pAudio->put_Volume(MIN_VOLUME);
		}
		else
		{
			// Restore previous volume setting
			hr = m_pAudio->put_Volume(m_lVolume);
		}
	}

	return hr;
}

//***Graph building***

//-----------------------------------------------------------------------------
// GraphManager::InitializeGraph
// Description: Create a new filter graph. (Tears down the old graph.) 
//-----------------------------------------------------------------------------
HRESULT GraphManager::InitializeGraph()
{
	HRESULT hr = S_OK;

	TearDownGraph();

	// Create the Filter Graph Manager.
	hr = CoCreateInstance(
		CLSID_FilterGraph, 
		NULL, 
		CLSCTX_INPROC_SERVER,
		IID_IGraphBuilder,
		(void**)&m_pGraph
		);

	// Query for graph interfaces.
	if (SUCCEEDED(hr))
	{
		hr = m_pGraph->QueryInterface(IID_IMediaControl, (void**)&m_pControl);
	}

	if (SUCCEEDED(hr))
	{
		hr = m_pGraph->QueryInterface(IID_IMediaEventEx, (void**)&m_pEvent);
	}

	if (SUCCEEDED(hr))
	{
		hr = m_pGraph->QueryInterface(IID_IBasicAudio, (void**)&m_pAudio);
	}

	// Set up event notification.
	if (SUCCEEDED(hr))
	{
		hr = m_pEvent->SetNotifyWindow((OAHWND)m_hwndEvent, m_eventMsg, NULL);
	}

	return hr;
}


//-----------------------------------------------------------------------------
// GraphManager::TearDownGraph
// Description: Tear down the filter graph and release resources. 
//-----------------------------------------------------------------------------

void GraphManager::TearDownGraph()
{
	// Stop sending event messages
	if (m_pEvent)
	{
		m_pEvent->SetNotifyWindow((OAHWND)NULL, NULL, NULL);
	}

	// Remove graph from ROT used for debugging
	RemoveGraphFromRot(m_TokenROT);

	SAFE_RELEASE(m_pGraph);
	SAFE_RELEASE(m_pControl);
	SAFE_RELEASE(m_pEvent);
	SAFE_RELEASE(m_pAudio);

    SAFE_DELETE(m_pVideo);

	m_playState = STATE_CLOSED;

    m_bAudioStream = FALSE;

}

HRESULT GraphManager::CreateVideoRenderer()
{
    HRESULT hr = S_OK;
 
    m_pVideo = new EVR();
       
    if (m_pVideo == NULL)
    {
        hr = E_OUTOFMEMORY;
    }

	if(SUCCEEDED(hr))
		hr = m_pVideo->AddToGraph(m_pGraph, m_hwndVideo);
	
	if(FAILED(hr))
        SAFE_DELETE(m_pVideo);
    
    return hr;
}

//-----------------------------------------------------------------------------
// RemoveUnconnectedRenderer
// Description: Remove a renderer filter from the graph if the filter is
//              not connected. 
//-----------------------------------------------------------------------------
HRESULT RemoveUnconnectedRenderer(IGraphBuilder *pGraph, IBaseFilter *pRenderer, BOOL *pbRemoved)
{
	IPin *pPin = NULL;

	BOOL bRemoved = FALSE;

	// Look for a connected input pin on the renderer.

	HRESULT hr = FindConnectedPin(pRenderer, PINDIR_INPUT, &pPin);
	SAFE_RELEASE(pPin);

	// If this function succeeds, the renderer is connected, so we don't remove it.
	// If it fails, it means the renderer is not connected to anything, so
	// we remove it.

	if (FAILED(hr))
	{
		hr = pGraph->RemoveFilter(pRenderer);
		bRemoved = TRUE;
	}

	if (SUCCEEDED(hr))
	{
		*pbRemoved = bRemoved;
	}

	return hr;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////
//
//	GraphManager::SetupCaptureGraph
//
//	This Function should replace the "RenderStreams" function from the original DShowPlayer implementation.
//  It sets up a complete graph including the HDPVR device filters and the Arcsoft decoders.
//
HRESULT GraphManager::SetupCaptureGraph()
{
	HRESULT hr = S_OK;

	IBaseFilter *pHDPVRCrossbar = NULL;  
	IBaseFilter *pHDPVRCapture =  NULL;  
	IBaseFilter *pHDPVREncoder =  NULL;  
	IBaseFilter *pMPEG2Demux =    NULL;  // {AFB6C280-2C41-11D3-8A60-0000F81E0E4A} - CLSID_MPEG2Demultiplexer
	IBaseFilter *pVideoDecoder =  NULL;  
	IBaseFilter *pAudioDecoder =  NULL;  
	IBaseFilter *pAudioRenderer = NULL;  // Default Audio Renderer - CLSID_DSoundRender

	// make sure our codec filter IDs have been assigned
	if (m_curVidDec == NULL || m_curAudDec == NULL)
	{
		hr = E_FAIL;
	}

	// Add the Capture Filters to the Graph
	if (SUCCEEDED(hr))
	{
		hr = AddCapDeviceToGraph(&pHDPVRCrossbar, &pHDPVRCapture, &pHDPVREncoder);
	}
		
	// Add MPEG2 Demux
	if (SUCCEEDED(hr))
	{
		hr = AddFilterByCLSID(m_pGraph, CLSID_MPEG2Demultiplexer, &pMPEG2Demux, L"MPEG-2 Demultiplexer");
	}

	// Add Video Decoder
	if (SUCCEEDED(hr))
	{
		hr = AddFilterByCLSID(m_pGraph, m_curVidDec->filterCLSID , &pVideoDecoder, L"MS Video Decoder");
	}

	// Add Audio Decoder
	if (SUCCEEDED(hr))
	{
		hr = AddFilterByCLSID(m_pGraph, m_curAudDec->filterCLSID , &pAudioDecoder, L"MS Audio Decoder");
	}

	// Add Audio Renderer
	if (SUCCEEDED(hr))
	{
		hr = AddFilterByCLSID(m_pGraph, CLSID_DSoundRender , &pAudioRenderer, L"Default DSound Renderer");
	}

	// Add EVR to the filter graph 
	if (SUCCEEDED(hr))
		hr = CreateVideoRenderer();

	if (SUCCEEDED(hr))
	{
		hr = ConfigureMPEG2Demux(&pMPEG2Demux);
	}	


	//**** Connect Pins of Filters in Graph ****

	//***CROSSBAR TO CAPTURE DEVICE***
	if (SUCCEEDED(hr))
	{		
		hr = ConnectFilters(&pHDPVRCrossbar, &pHDPVRCapture, 0, 0);   // Video decoder out - Virtual video in
	}

	if (SUCCEEDED(hr))
	{
		hr = ConnectFilters(&pHDPVRCrossbar, &pHDPVRCapture, 1, 1); //  Audio Decoder Out - Virtual Audio In
	}
	//***END CROSSBAR TO CAPTURE DEVICE****

	//***CAPTURE DEVICE TO ENCODER*****
	if (SUCCEEDED(hr))
	{
		hr = ConnectFilters(&pHDPVRCapture, &pHDPVREncoder, 0, 0);	//  Virtual Video Out - Analog Video In
	}

	if (SUCCEEDED(hr))
	{
		hr = ConnectFilters(&pHDPVRCapture, &pHDPVREncoder, 1, 1); //  Virtual Audio Out - Analog Audio In
	}
	//****END CAPTURE DEVICE TO ENCODER****

	//****ENCODER TO MPEG2DEMUX*****
	if (SUCCEEDED(hr))
	{
		hr = ConnectFilters(&pHDPVREncoder, &pMPEG2Demux, 0, 0);	// H264 Out - MPEG-2 Stream In
	}

	//*****END ENCODER TO MPEG2DEMUX*****

	//*****MPEG2DEMUX TO VIDEO DECODER****
	if (SUCCEEDED(hr))
	{
		hr = ConnectFilters(&pMPEG2Demux, &pVideoDecoder, 1, 0);	// Video Out - Video In
	}
	//*****END MPEG2DEMUX to VIDEO DECODER*****

	//****MPEG2DEMUX TO AUDIO DECODER*****
	if (SUCCEEDED(hr))
	{
		hr = ConnectFilters(&pMPEG2Demux, &pAudioDecoder, 0, 0);	// Audio Out - Audio In
	}
	//****END MPEG2DEMUX to AUDIO DECODER*****

	//**** AUDIO DECODER TO SOUND RENDERER *****  
	if (SUCCEEDED(hr))
	{
		hr = ConnectFilters(&pAudioDecoder, &pAudioRenderer, 0, 0);	
	}
	if (SUCCEEDED(hr))
	{
		m_bAudioStream = TRUE;  // We have successfully connected the audio renderer
	}
	//*** END AUDIO DECODER TO SOUND RENDERER******               */

	//****CONNECT TO VIDEO RENDERER USING RENDEREX************
	IFilterGraph2* pGraph2 = NULL;
	IPin* pOut = NULL;

	// get the filtergraph2 interface
	if (SUCCEEDED(hr))
		hr = m_pGraph->QueryInterface(IID_IFilterGraph2, (void**)&pGraph2);

	if (SUCCEEDED(hr))
		hr = FindPinByIndex(pVideoDecoder, PINDIR_OUTPUT, 0, &pOut);
	
	// attempt to connect to the EVR
	if (SUCCEEDED(hr))
		pGraph2->RenderEx(pOut, AM_RENDEREX_RENDERTOEXISTINGRENDERERS, NULL);

	SAFE_RELEASE(pOut);
	//*****END CONNECT TO VIDEO RENDERER********
	
	//*****END CONNECT FILTERS*****

	SAFE_RELEASE(pGraph2);
	SAFE_RELEASE(pHDPVRCrossbar); 
	SAFE_RELEASE(pHDPVRCapture);
	SAFE_RELEASE(pHDPVREncoder); 
	SAFE_RELEASE(pMPEG2Demux); 
	SAFE_RELEASE(pVideoDecoder);  
	SAFE_RELEASE(pAudioDecoder);   
	SAFE_RELEASE(pAudioRenderer);

	return hr;
}


///////////////////////////////////////////////////////////////////////////////////////////
//
//	GraphManager::AddCapDeviceToGraph
//
//
//
HRESULT GraphManager::AddCapDeviceToGraph(IBaseFilter** pCrossBar, IBaseFilter** pCapDevice, IBaseFilter** pEncoder)
{
	HRESULT hr = S_OK;

	IBaseFilter *pCrb = NULL;
	IBaseFilter *pCap = NULL;
	IBaseFilter *pEnc = NULL;

	if (m_curXBar == NULL || m_curCapDevice ==NULL || m_curEncoder == NULL)
	{
		// one of the encoder filters is not set, return failed HR
		hr = E_FAIL;
	}

	if (SUCCEEDED(hr))
	{
		hr = AddFilterFromMoniker(m_pGraph, m_curXBar->pFilterMoniker, &pCrb, L"Hauppauge HD PVR Crossbar");
	}

	if (SUCCEEDED(hr))
	{
		hr = AddFilterFromMoniker(m_pGraph, m_curCapDevice->pFilterMoniker, &pCap, L"Hauppauge HD PVR Capture Device");	
	}
	
	if (SUCCEEDED(hr))
	{
		hr = AddFilterFromMoniker(m_pGraph, m_curEncoder->pFilterMoniker, &pEnc, L"Hauppauge HD PVR Encoder");
	}

	if (SUCCEEDED(hr))
	{	
 		// all filters added, return base filter pointers to caller
		
		*pCrossBar = pCrb;
		(*pCrossBar)->AddRef();

		*pCapDevice = pCap;
		(*pCapDevice)->AddRef();

		*pEncoder = pEnc;
		(*pEncoder)->AddRef();
		
	}

	SAFE_RELEASE(pCrb);
	SAFE_RELEASE(pCap);
	SAFE_RELEASE(pEnc);

	return hr;
}

HRESULT GraphManager::ConfigureMPEG2Demux(IBaseFilter** mpeg2Demux)
{
	HRESULT hr = S_OK;

	IPin* pVidPin = NULL;
	IPin* pAudPin = NULL;

	//***INIT VIDEO MEDIA TYPE***  (H264)
	AM_MEDIA_TYPE videoMT;			
	VIDEOINFOHEADER *pVideoInfo = NULL;
		
	ZeroMemory(&videoMT, sizeof(AM_MEDIA_TYPE));  // fill the structure with zeros, so we don't have garbage info
	videoMT.majortype = MEDIATYPE_Video;
	videoMT.subtype = MEDIASUBTYPE_H264; 
	videoMT.formattype = FORMAT_TYPE_VIDEOINFO;
	videoMT.bFixedSizeSamples = false;
	videoMT.bTemporalCompression = true;
	videoMT.cbFormat = sizeof(VIDEOINFOHEADER);
	videoMT.pbFormat = (BYTE*)CoTaskMemAlloc(videoMT.cbFormat);

	//***INIT AUDIO MEDIA TYPE***  
	AM_MEDIA_TYPE audioMT;			
	WAVEFORMATEX *pWF;

	ZeroMemory(&audioMT, sizeof(AM_MEDIA_TYPE));
	audioMT.majortype = MEDIATYPE_Audio;
	audioMT.subtype = MEDIASUBTYPE_MPEG_ADTS_AAC;   // AAC
	//audioMT.subtype = MEDIASUBTYPE_DOLBY_AC3;         // AC3
	audioMT.formattype = FORMAT_TYPE_WAVEFORMATEX;
	audioMT.cbFormat = sizeof(WAVEFORMATEX);
	audioMT.pbFormat = (BYTE*)CoTaskMemAlloc(audioMT.cbFormat);

	// Make sure both the audio and video format block pointers were properly allocated
	if (videoMT.pbFormat == NULL || audioMT.pbFormat == NULL)
		hr = E_OUTOFMEMORY;
	
	if (SUCCEEDED(hr))
	{
		// Add the pertinent video format block info
		ZeroMemory(videoMT.pbFormat, videoMT.cbFormat); 
		pVideoInfo = (VIDEOINFOHEADER*)videoMT.pbFormat;
		RECT rcSRC = {0, 0, 0, 0};
		RECT rcDS = {0, 0, 0, 0};
		pVideoInfo->rcSource = rcSRC;
		pVideoInfo->rcTarget = rcDS;
			
		// Add the pertinent audio format block info   
		ZeroMemory(audioMT.pbFormat, audioMT.cbFormat);
		pWF = (WAVEFORMATEX*)audioMT.pbFormat;

		//***REQUIRED FOR 2 Channel AAC AUDIO TYPES****
		pWF->wFormatTag = WAVE_FORMAT_MPEG_ADTS_AAC;
		pWF->nSamplesPerSec = 48000;        // 48.0 KHz
		pWF->nChannels = 2;                 // only 2 channel audio in AAC
		pWF->nBlockAlign = 4;               // (nChannels * wBitsPerSample) / 8
		pWF->nAvgBytesPerSec = 192000;      // nSamplesPerSec * nBlockAlign
		pWF->wBitsPerSample = 16;
	}
	

	//***Enumerate Demultiplexer Pins and map PIDs (video 0x1011, audio 0x1100)***
	IMPEG2PIDMap *pVidPidMap = NULL;
	IMPEG2PIDMap *pAudPidMap = NULL;
	IMpeg2Demultiplexer *pTmpDemux = NULL;
	ULONG vidPID = 0x1011;
	ULONG audPID = 0x1100;

	hr = (*mpeg2Demux)->QueryInterface(IID_IMpeg2Demultiplexer, (void**)&pTmpDemux);

	// create video pin
	if (SUCCEEDED(hr))
		hr = pTmpDemux->CreateOutputPin(&videoMT, L"Video", &pVidPin);
		
	// map video PID
	if (SUCCEEDED(hr))
	{
		hr = pVidPin->QueryInterface(IID_IMPEG2PIDMap, (void**)&pVidPidMap);
	}
	if (SUCCEEDED(hr))
	{
		hr = pVidPidMap->MapPID(1, &vidPID, MEDIA_ELEMENTARY_STREAM);
	}

	// create audio pin
	if(SUCCEEDED(hr))
	{
		hr = pTmpDemux->CreateOutputPin(&audioMT, L"Audio", &pAudPin);
	}
		
	// map audio PID
	if (SUCCEEDED(hr))
	{
		hr = pAudPin->QueryInterface(IID_IMPEG2PIDMap, (void**)&pAudPidMap);
	}
	if (SUCCEEDED(hr))
	{
		hr = pAudPidMap->MapPID(1, &audPID, MEDIA_ELEMENTARY_STREAM);
	}
	
	SAFE_RELEASE(pVidPin);
	SAFE_RELEASE(pAudPin);
	SAFE_RELEASE(pVidPidMap);
	SAFE_RELEASE(pAudPidMap);
	SAFE_RELEASE(pTmpDemux);

	return hr;
	
}

HRESULT GraphManager::ConnectFilters(IBaseFilter** outputFilter, IBaseFilter** inputFilter, const UINT& outPinIndex, const UINT& inPinIndex)
{
	HRESULT hr = S_OK;

	IPin* pOut = NULL;
	IPin* pIn =  NULL;

	
	hr = FindPinByIndex(*outputFilter, PINDIR_OUTPUT, outPinIndex, &pOut);  // Video Decoder Out
	
	if (SUCCEEDED(hr))
	{
		hr = FindPinByIndex(*inputFilter, PINDIR_INPUT, inPinIndex, &pIn); // Virtual Video In
	}

	if (SUCCEEDED(hr))
	{
		hr = m_pGraph->Connect(pOut, pIn);   // connect 'em
	}

	SAFE_RELEASE(pOut);
	SAFE_RELEASE(pIn);

	return hr;

}